﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.ServiceModel.Channels;
using System.Text;
using Kugar.Core.Collections;
using System.Threading;
using Kugar.Core.BaseStruct;
using Kugar.Core.Exceptions;
using Kugar.Core.ExtMethod;
using System.Collections.Concurrent;

namespace Kugar.Core.Communications.Transfer
{
    internal class UDPFactory : TransferFactoryBase
    {
        private ConcurrentDictionary<int, UDPManager> _cacheUDPManager = new ConcurrentDictionary<int, UDPManager>();
        private object lockerObj = new object();

        public UDPFactory()
        {

        }

        public override void Listen(TransferConnectConfig config)
        {
            if (!(config is UDPConnectConfig))
            {
                throw new ArgumentException("UDP连接的创建,请传入UDPConnectConfig类型的配置类", "config");
            }

            var udpConfig = (UDPConnectConfig)config;

            if (udpConfig.LocalPort < 0)
            {
                throw new ArgumentOutOfRangeException("config", "端口号必须大于0");
            }

            UDPManager udpManager = getUDPManager(udpConfig.LocalPort, config.BufferLength);

            //lock (lockerObj)
            //{
            //    if (!_cacheUDPManager.TryGetValue(port, out udpManager))
            //    {
            //        udpManager = new UDPManager(port);
            //        _cacheUDPManager.Add(port, udpManager);
            //    }
            //}

            if (udpManager != null)
            {
                udpManager.Listen();
            }
        }

        public override void StopListen(TransferConnectConfig port)
        {
            if (!(port is UDPConnectConfig))
            {
                throw new ArgumentTypeNotMatchException("port", "UDPConnectConfig");
            }

            var udpConfig = (UDPConnectConfig)port;

            lock (lockerObj)
            {
                UDPManager udpManager = null;

                if (_cacheUDPManager.TryGetValue(udpConfig.LocalPort, out udpManager))
                {
                    udpManager.StopListen();
                }
            }
        }

        public override void ClosePort(TransferConnectConfig port)
        {
            if (!(port is UDPConnectConfig))
            {
                throw new ArgumentTypeNotMatchException("port", "UDPConnectConfig");
            }

            var udpConfig = (UDPConnectConfig)port;

            lock (lockerObj)
            {
                UDPManager udpManager = null;

                if (_cacheUDPManager.TryGetValue(udpConfig.LocalPort, out udpManager))
                {
                    udpManager.Close();
                }
            }
        }

        #region "ConnectToAsync"

        public override bool ConnectToAsync(TransferAsyncEventArgs ea, TransferConnectConfig config)
        {
            if (ea == null)
            {
                throw new ArgumentNullException("ea");
            }

            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            var udpConfig = (UDPConnectConfig)config;

            UDPManager udpManager = getUDPManager(udpConfig.LocalPort, config.BufferLength);

            return udpManager.ConnectToAsync(ea, udpConfig);

            //SerialPortTransferBase serialPortTransfer = null;

            //try
            //{
            //    serialPortTransfer = getConnectOrBuild((SerialPortConnectConfig)config);
            //}
            //catch (Exception ex)
            //{
            //    ea.Error = new TransferException(ex);
            //}

            //ea.Transfer = serialPortTransfer;

            //return false;

        }


        #endregion

        //public override ITransfer ConnectTo(TransferConnectConfig config, ref Exception error)
        //{
        //    if (!(config is UDPConnectConfig))
        //    {
        //        throw new ArgumentException("UDP连接的创建,请传入UDPConnectConfig类型的配置类", "config");
        //    }

        //    var udpConfig = (UDPConnectConfig)config;

        //    var udpManager = getUDPManager(udpConfig.LocalPort, config.BufferLength);

        //    if (udpManager != null)
        //    {
        //        //return udpManager.ConnectTo(udpConfig.RemotePoint);

        //        return null;
        //    }
        //    else
        //    {
        //        return null;
        //    }
        //}

        //public override IAsyncResult BeginConnectTo(TransferConnectConfig config, AsyncCallback callback, object state)
        //{
        //    if (!(config is UDPConnectConfig))
        //    {
        //        throw new ArgumentException("UDP连接的创建,请传入UDPConnectConfig类型的配置类", "config");
        //    }

        //    var udpConfig = (UDPConnectConfig)config;

        //    UDPManager udpManager = getUDPManager(udpConfig.LocalPort, config.BufferLength);

        //    return udpManager.BeginConnectTo(udpConfig.RemotePoint, callback, state);

        //    //lock (lockerObj)
        //    //{
        //    //    if (!_cacheUDPManager.TryGetValue(localPort, out udpManager))
        //    //    {
        //    //        udpManager = new UDPManager(localPort);
        //    //        _cacheUDPManager.Add(localPort, udpManager);
        //    //    }

        //    //    if (udpManager != null)
        //    //    {
        //    //        return udpManager.BeginConnectTo(remotePoint,callback,state);
        //    //    }
        //    //    else
        //    //    {
        //    //        throw new Exception("端口已被占用");
        //    //    }

        //    //}
        //}

        //public override ITransfer EndConnectTo(IAsyncResult iar, ref Exception error)
        //{
        //    var ar = (UDPManager.UDPConnectAsyncResult)iar;

        //    UDPManager udpManager = null;

        //    lock (lockerObj)
        //    {
        //        if (_cacheUDPManager.TryGetValue(ar.LocalPort, out udpManager))
        //        {
        //            return udpManager.EndConnectTo(iar, ref error);
        //        }


        //    }

        //    return null;


        //    //lock (lockerObj)
        //    //{
        //    //    if (_cacheUDPManager.TryGetValue(ar.LocalPort, out udpManager))
        //    //    {
        //    //        return udpManager.EndConnectTo(iar,ref error);
        //    //    }
        //    //    else
        //    //    {
        //    //        return null;
        //    //    }

        //    //}
        //}

        public override void Close()
        {
            lock (lockerObj)
            {
                foreach (var udpManager in _cacheUDPManager)
                {
                    udpManager.Value.Close();
                }

                _cacheUDPManager.Clear();
            }
        }

        //public event EventHandler<RemoteRequestConnectEventArgs> ConnectRequest;

        //public event EventHandler<RemoteConenctSuccessEventArgs> ConnectSuccessed;

        private UDPManager getUDPManager(int localPort, int bufferLength)
        {
            UDPManager udpManager = null;

            lock (lockerObj)
            {

                udpManager = _cacheUDPManager.GetOrAdd(localPort, (port) =>
                                                         {
                                                             var temp = new UDPManager(localPort, bufferLength);

                                                             temp.ConnectRequest += (s, e) => OnConnectRequest(e);
                                                             temp.ConnectSuccessed += (s, e) => OnConnectSuccessed(e);

                                                             return temp;
                                                         });

                //if (!_cacheUDPManager.TryGetValue(localPort, out udpManager))
                //{
                //    udpManager = new UDPManager(localPort, bufferLength);
                //    _cacheUDPManager.Add(localPort, udpManager);
                //    udpManager.ConnectRequest += (s, e) => OnConnectRequest(e);
                //    udpManager.ConnectSuccessed += (s, e) => OnConnectSuccessed(e);
                //}

                if (udpManager != null)
                {
                    return udpManager;
                }
                else
                {
                    throw new Exception("端口已被占用");
                }

            }
        }
    }

    internal class UDPManager
    {
        private bool _enableAccept = false;
        private AutoTimeoutDictionary<IPEndPoint, UDPItemInternal> _cacheUDPConnection = new AutoTimeoutDictionary<IPEndPoint, UDPItemInternal>();
        private int _localPort = 0;
        private Socket _socket = null;
        private BufferManager _bufferManager = null;// BufferManager.CreateBufferManager(2*1024*1024,8192);
        private bool _isClosed = false;
        private HashSet<IAsyncResult> _cacheAsyncResult = new HashSet<IAsyncResult>();

        private SocketAsyncEventArgs _internalReceiveAsync = new SocketAsyncEventArgs();
        private SocketAsyncEventArgsPool _socketAsyncPool = null;
        private byte[] _revBuffer = new byte[8192];

        public UDPManager(int localPort, int bufferLength)
        {
            var maxLengthPool = Math.Max(2 * 1024 * 1024, bufferLength * 20);

            _bufferManager = BufferManager.CreateBufferManager(maxLengthPool, bufferLength * 2);

            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _socket.Bind(new IPEndPoint(IPAddress.Loopback, localPort));
            _localPort = localPort;
            _cacheUDPConnection.Timeout += CacheUDPConnection_Timeout;
            _enableAccept = false;

            _internalReceiveAsync.SetBuffer(_revBuffer, 0, _revBuffer.Length);
            _internalReceiveAsync.Completed += SocketDataReceive;

            OnBeginRev();
        }


        public void Listen()
        {
            _enableAccept = true;


        }

        public void StopListen()
        {
            _enableAccept = false;
        }

        public void Close()
        {
            StopListen();

            _socket.Close();

            _cacheUDPConnection.Close();
        }

        public int LocalPort
        {
            get { return _localPort; }
        }

        #region "SendAsync"

        public bool SendAsync(TransferAsyncEventArgs ea, IPEndPoint remotePoint)
        {
            var retValue = true;

            if (_isClosed)//!Socket.Connected || )
            {
                ea.Error = new TransferClosedException();
                retValue = false;
            }

            UDPItemInternal udpItem = null;

            IAutoTimeoutDictionaryItem<IPEndPoint, UDPItemInternal> t = null;

            if (_cacheUDPConnection.TryGetValue(remotePoint, out t))
            {
                udpItem = t.Value;
            }
            else
            {
                retValue = false;
                ea.Error = new TransferClosedException();
            }

            if (retValue)
            {
                try
                {
                    var socketEa = GetSendEventArgs(ea.Buffer, ea.Offset, ea.Count);

                    socketEa.UserToken = ea;
                    socketEa.RemoteEndPoint = remotePoint;

                    udpItem.OnBeforceSendData(ea);

                    retValue = _socket.SendToAsync(socketEa);

                    if (!retValue)
                    {
                        if (socketEa.SocketError == SocketError.Success)
                        {
                            ea.BytesTransferred = socketEa.BytesTransferred;

                            ea.Error = null;

                            socketEa.Completed -= OnSendAsyncCompleted;

                            socketEa.Dispose();

                        }
                        else
                        {
                            ea.Error = new Exception(socketEa.SocketError.ToString());
                        }

                    }
                }
                catch (ObjectDisposedException)
                {
                    ea.Error = new TransferClosedException();
                    retValue = false;
                }
                catch (SocketException)
                {
                    ea.Error = new TransferDisconnectedException();
                    retValue = false;
                }

                if (!retValue && ea.Error == null)
                {
                    udpItem.OnAfterSendData(ea);
                }
            }

            return retValue;
        }

        private void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e)
        {
            using (e)
            {
                e.Completed -= OnSendAsyncCompleted;

                var ea = (TransferAsyncEventArgs)e.UserToken;

                ea.BytesTransferred = e.BytesTransferred;

                ea.OnCompleted(this);

                IAutoTimeoutDictionaryItem<IPEndPoint, UDPItemInternal> udpItem = null;

                if (_cacheUDPConnection.TryGetValue((IPEndPoint)e.RemoteEndPoint, out udpItem))
                {
                    udpItem.Value.OnAfterSendData(ea);
                }

                //base.OnAfterSendData(ea.Buffer, ea.Offset, ea.Count, ea.BytesTransferred, ea.Error);
            }



        }

        private SocketAsyncEventArgsEx GetSendEventArgs(byte[] data, int offset, int count)
        {
            var temp = _socketAsyncPool.Take();

            temp.SetBuffer(data, offset, count);

            //ClearSocketAsyncEventArgs(temp);

            temp.Completed += OnSendAsyncCompleted;

            return temp;
        }

        #endregion

        #region "ConnectToAsync"

        public bool ConnectToAsync(TransferAsyncEventArgs ea, UDPConnectConfig config)
        {
            if (ea == null)
            {
                throw new ArgumentNullException("ea");
            }

            IAutoTimeoutDictionaryItem<IPEndPoint, UDPItemInternal> udpItem = null;

            lock (_cacheUDPConnection)
            {
                if (!_cacheUDPConnection.TryGetValue(config.RemotePoint, out udpItem))
                {
                    udpItem = _cacheUDPConnection.Add(config.RemotePoint, new UDPItemInternal(this, _bufferManager, config.RemotePoint));
                }
            }

            ea.Transfer = udpItem.Value;

            ea.Error = null;

            return false;
        }


        #endregion



        public ITransfer ConnectTo(UDPConnectConfig config)
        {
            var t = new UDPItemInternal(this, _bufferManager, config.RemotePoint);

            _cacheUDPConnection.Add(config.RemotePoint, t);

            return t;
        }

        public IAsyncResult BeginConnectTo(IPEndPoint remotePoint, AsyncCallback callback, object state)
        {
            var ar = new UDPConnectAsyncResult(_localPort, callback, state);

            lock (_cacheAsyncResult)
            {
                _cacheAsyncResult.Add(ar);
            }

            IAutoTimeoutDictionaryItem<IPEndPoint, UDPItemInternal> udpItem = null;

            lock (_cacheUDPConnection)
            {
                if (!_cacheUDPConnection.TryGetValue(remotePoint, out udpItem))
                {
                    udpItem = _cacheUDPConnection.Add(remotePoint, new UDPItemInternal(this, _bufferManager, remotePoint));
                }
            }

            if (udpItem != null)
            {
                ar.Execute(udpItem.Value, null);
            }

            return ar;
        }

        public ITransfer EndConnectTo(IAsyncResult iar, ref Exception error)
        {
            _cacheAsyncResult.Remove(iar);

            if (iar is UDPConnectAsyncResult)
            {
                var ar = (UDPConnectAsyncResult)iar;

                error = ar.Error;

                return ar.UDPItem;
            }
            else
            {
                error = null;
                return null;
            }
        }

        public event EventHandler<RemoteRequestConnectEventArgs> ConnectRequest;

        public event EventHandler<RemoteConenctSuccessEventArgs> ConnectSuccessed;

        private void Remove(UDPItemInternal item)
        {
            _cacheUDPConnection.Remove(item);
        }

        internal IAsyncResult BeginSend(byte[] data, int offset, int count, IPEndPoint remotePoint, AsyncCallback callback, object state)
        {
            SocketFlags flag;

            if (remotePoint.Address == IPAddress.Broadcast)
            {
                flag = SocketFlags.Broadcast;
            }
            else
            {
                flag = SocketFlags.None;
            }

            return _socket.BeginSendTo(data, offset, count, flag, remotePoint, callback, state);
        }

        internal int EndSend(IAsyncResult iar, ref Exception error)
        {
            var sendCount = 0;

            try
            {
                sendCount = _socket.EndSendTo(iar);
            }
            catch (Exception ex)
            {
                error = ex;
            }

            return sendCount;
        }

        private bool OnConnectRequest(IPEndPoint remotePoint)
        {
            if (!_enableAccept)
            {
                return false;
            }

            var t = new RemoteRequestConnectEventArgs(remotePoint, null);

            if (ConnectRequest != null)
            {
                ConnectRequest(this, t);
            }

            return t.IsEnableConnect;

        }

        private void OnConnectSuccess(IPEndPoint remotePoint, ITransfer transfer)
        {
            Events.EventHelper.RaiseAsync(ConnectSuccessed, this, () => new RemoteConenctSuccessEventArgs(transfer));
        }

        private void OnBeginRev()
        {
            _internalReceiveAsync.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

            _socket.ReceiveFromAsync(_internalReceiveAsync);
        }

        private void SocketDataReceive(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                var tempBuffer = _bufferManager.TakeBuffer(e.BytesTransferred);

                Buffer.BlockCopy(e.Buffer, e.Offset, tempBuffer, 0, e.BytesTransferred);

                if (!_isClosed)
                {
                    OnBeginRev();
                }

                IAutoTimeoutDictionaryItem<IPEndPoint, UDPItemInternal> udpItem = null;

                var incomeIPEndPoint = (IPEndPoint)e.RemoteEndPoint;

                if (!_cacheUDPConnection.TryGetValue(incomeIPEndPoint, out udpItem))
                {
                    if (OnConnectRequest(incomeIPEndPoint))
                    {
                        udpItem = _cacheUDPConnection.Add(incomeIPEndPoint, new UDPItemInternal(this, _bufferManager, incomeIPEndPoint));

                        OnConnectSuccess(incomeIPEndPoint, udpItem.Value);

                    }
                }

                if (udpItem != null)
                {
                    udpItem.Active();

                    Exception error = null;

                    if (e.SocketError != SocketError.Success)
                    {
                        error = new TargetException(e.SocketError.ToStringEx());
                    }

                    udpItem.Value.DataReceive(tempBuffer, 0, e.BytesTransferred, error);
                }
            }
        }

        void CacheUDPConnection_Timeout(object sender, AutoTimeoutDictionaryItemEventArgs<IPEndPoint, UDPItemInternal> e)
        {
            if (e.TimeoutCount > 30)
            {
                _cacheUDPConnection.Remove(e.Key);
            }
        }

        internal class UDPItemInternal : TransferBase, INetTransfer
        {
            private UDPManager _udpManager = null;
            private IPEndPoint _remotePoint = null;
            private bool _isStop;
            private Dictionary<IAsyncResult, ArraySegment<byte>> _cacheSendAsyncResult = new Dictionary<IAsyncResult, ArraySegment<byte>>();
            private volatile UDPConnectReceiveAsyncResult _receiveAsyncResult = null;
            private CircularQueue<ArraySegment<byte>> _cacheReceiveData = new CircularQueue<ArraySegment<byte>>(2);
            private BufferManager _bufferManager = null;
            private TransferAsyncEventArgs _receiveAsync = null;

            public UDPItemInternal(UDPManager udpManager, BufferManager bufferManager, IPEndPoint remotePoint)
            {
                _udpManager = udpManager;
                _remotePoint = remotePoint;
                _isStop = false;
                _bufferManager = bufferManager;
            }

            internal void DataReceive(byte[] data, int offset, int count, Exception error)
            {
                if (_isStop)
                {
                    return;
                }

                var old = _cacheReceiveData.Enqueue(new ArraySegment<byte>(data, offset, count));

                if (old.HasData())
                {
                    _bufferManager.ReturnBuffer(old.Array);
                }

                var t = OnDataReceiveInternal(null);

                if (t != null)
                {
                    t.OnCompleted(this);
                }

                //this.OnAfterReceiveData(new ArraySegment<byte>(data, offset, count), error);
            }

            #region "SendAsync"

            public override bool SendAsync(TransferAsyncEventArgs ea)
            {
                if (ea == null)
                {
                    throw new ArgumentNullException("ea");
                }

                if (!ea.Buffer.IsInEnableRange(ea.Offset, ea.Count))
                {
                    throw new ArgumentOutOfRangeException("ea");
                }

                var retValue = true;

                try
                {
                    retValue = _udpManager.SendAsync(ea, _remotePoint);
                }
                catch (Exception ex)
                {
                    ea.Error = new TransferException(ex);
                    retValue = false;
                }

                return retValue;
            }

            internal virtual void OnBeforceSendData(TransferAsyncEventArgs e)
            {
                base.OnBeforceSendData(e);
            }

            internal virtual void OnAfterSendData(TransferAsyncEventArgs e)
            {
                base.OnAfterSendData(e);
            }

            #endregion

            #region "ReceiveAsync"

            public override bool ReceiveAsync(TransferAsyncEventArgs ea)
            {
                if (ea == null)
                {
                    throw new ArgumentNullException("ea");
                }

                if (ea.Buffer == null)
                {
                    throw new ArgumentNullException("ea.Buffer");
                }

                var retValue = true;

                if (Interlocked.CompareExchange(ref _receiveAsync, ea, null) == null)
                {
                    if (OnDataReceiveInternal(null) != null)
                    {
                        retValue = false;
                    }
                }
                else
                {
                    //TODO:已有线程正在接收数据中,无法再接收
                    retValue = false;
                }

                return retValue;
            }

            #endregion



            #region Overrides of TransferBase

            //public override IAsyncResult BeginSend(byte[] data, int offset, int count, AsyncCallback callback, object state)
            //{
            //    if (_isStop)
            //    {
            //        return null;
            //    }

            //    var sendStruct = new ArraySegment<byte>(data, offset, count);

            //    OnBeforceSendData(data, offset, count, null);

            //    var iar = _udpManager.BeginSend(sendStruct.Array, sendStruct.Offset, sendStruct.Offset, _remotePoint, callback, state);

            //    _cacheSendAsyncResult.Add(iar, sendStruct);

            //    return iar;
            //}

            //public override int EndSend(IAsyncResult iar, ref Exception error)
            //{
            //    var sendCount = _udpManager.EndSend(iar, ref error);

            //    lock (_cacheSendAsyncResult)
            //    {
            //        if (_cacheSendAsyncResult.ContainsKey(iar))
            //        {
            //            //OnAfterSendData(_cacheSendAsyncResult[iar], sendCount, error);

            //            _cacheSendAsyncResult.Remove(iar);
            //        }
            //    }

            //    return sendCount;
            //}

            //public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
            //{
            //    if (callback == null)
            //    {
            //        throw new ArgumentNullException("callback");
            //    }

            //    if (count <= 0)
            //    {
            //        throw new ArgumentOutOfRangeException("count");
            //    }

            //    if (offset <= 0)
            //    {
            //        throw new ArgumentOutOfRangeException("offset");
            //    }

            //    if (buffer == null)
            //    {
            //        throw new ArgumentNullException("buffer");
            //    }

            //    if (buffer.IsInEnableRange(offset, count))
            //    {
            //        throw new ArgumentOutOfRangeException("buffer");
            //    }

            //    var iar = new UDPConnectReceiveAsyncResult(this, buffer, offset, count, callback, state);

            //    if (Interlocked.CompareExchange(ref _receiveAsyncResult, iar, null) != null)
            //    {
            //        throw new Exception("链接正在接收中，不允许再次调用BeginReceive");
            //    }

            //    //_asyncResult = iar;

            //    //ThreadPool.QueueUserWorkItem(OnDataReceiveInternal, iar);

            //    return iar;
            //}

            //public int EndReceive(IAsyncResult iar, ref Exception error)
            //{
            //    if (_receiveAsyncResult == null || _receiveAsyncResult != iar)
            //    {
            //        error = new Exception("传入的Iar参数错误");
            //        return -1;
            //    }

            //    var count = _receiveAsyncResult.Count;

            //    Interlocked.Exchange(ref _receiveAsyncResult, null);

            //    //var data = (ConnectReceiveAsyncResult) _asyncResult;

            //    return count;
            //}

            public override bool IsStop
            {
                get { return _isStop; }
            }

            public override int ReceiveBufferCount
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            public override bool IsActivity
            {
                get { return true; }
            }

            protected override void OnClose()
            {
                Stop();

                _udpManager.Remove(this);
            }

            protected override bool OnStart()
            {
                _isStop = false;

                return _isStop;
            }

            protected override bool OnStop()
            {
                _isStop = true;

                //if (_receiveAsyncResult != null)
                //{
                //    _receiveAsyncResult.Execute(new ArraySegment<byte>(null, 0, 0), new Exception("连接暂停"));
                //}

                //_receiveAsyncResult = null;

                if (_receiveAsync != null)
                {
                    var temp = Interlocked.Exchange(ref _receiveAsync, null);

                    if (temp != null)
                    {
                        temp.Error = new TransferStopedException();
                        temp.OnCompleted(this);
                    }
                }

                _cacheSendAsyncResult.Clear();

                var t = _cacheReceiveData.Clear();

                if (t != null)
                {
                    foreach (var segment in t)
                    {
                        _bufferManager.ReturnBuffer(segment.Array);
                    }
                }



                return _isStop;
            }

            #endregion

            #region Implementation of INetTransfer

            public int LocalPort
            {
                get { return _udpManager.LocalPort; }
            }

            public IPEndPoint RemotePoint
            {
                get { return _remotePoint; }
            }

            #endregion

            private TransferAsyncEventArgs OnDataReceiveInternal(object state)
            {
                ArraySegment<byte> data;

                if (_receiveAsync != null && _cacheReceiveData.TryDequeue(out data))
                {
                    var temp = _receiveAsync;

                    temp = Interlocked.CompareExchange(ref _receiveAsync, null, temp);

                    var copyCount = Math.Min(data.Count, temp.Count);

                    Buffer.BlockCopy(data.Array, data.Offset, temp.Buffer, temp.Offset, copyCount);

                    _bufferManager.ReturnBuffer(data.Array);

                    temp.BytesTransferred = copyCount;

                    base.OnAfterReceiveData(temp);

                    return temp;
                }
                else
                {
                    return null;
                }
            }

            private class UDPConnectReceiveAsyncResult : AsyncResultBase
            {
                private byte[] _buffer;
                private int _count;
                private UDPItemInternal _conn;
                private int _offset;

                public UDPConnectReceiveAsyncResult(UDPItemInternal conn, byte[] buffer, int offset, int count, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    if (buffer.IsInEnableRange(offset, count))
                    {
                        throw new ArgumentOutOfRangeException("buffer");
                    }

                    _conn = conn;
                    _buffer = buffer;
                    _count = count;
                    _offset = offset;
                }

                public int Count
                {
                    get { return _count; }
                }

                public void Execute(ArraySegment<byte> data, Exception error)
                {

                    int copyCount = 0;

                    if (error == null)
                    {
                        if (data.Count > 0)
                        {

                            copyCount = Math.Min(data.Count, _count);

                            Array.Copy(data.Array, data.Offset, _buffer, _offset, copyCount);
                        }
                    }

                    _count = copyCount;

                    if (error == null)
                    {
                        //var e = new DataReceiveEventArgs(, _conn.Item, DateTime.Now, error);

                        //_conn.OnAfterReceiveData(new ArraySegment<byte>(_buffer, 0, copyCount), error);
                    }

                    base.Execute(error);

                }

            }
        }

        internal class UDPConnectAsyncResult : AsyncResultBase
        {
            private UDPItemInternal _udpItem = null;
            private int _localPort = 0;

            public UDPConnectAsyncResult(int localPort, AsyncCallback callback, object state)
                : base(callback, state)
            {
                _localPort = localPort;
            }

            public void Execute(UDPItemInternal udpItem, Exception error)
            {
                _udpItem = udpItem;
                base.Execute(error);
            }

            public int LocalPort
            {
                get { return _localPort; }
            }

            public UDPItemInternal UDPItem
            {
                get { return _udpItem; }
            }
        }
    }



    public class UDPConnectConfig : TransferConnectConfig
    {
        public UDPConnectConfig(int localPort, IPEndPoint remotePoint)
        {
            if (localPort <= 0)
            {
                throw new ArgumentOutOfRangeException("localPort");
            }

            if (remotePoint == null)
            {
                throw new ArgumentNullException("remotePoint");
            }

            LocalPort = localPort;
            RemotePoint = remotePoint;
            BufferLength = 8192;
        }

        public int LocalPort { set; get; }

        public IPEndPoint RemotePoint { set; get; }

        public override TransferProtocolType Protocol
        {
            get { return TransferProtocolType.UDP_Normal; }
        }
    }

    public class UDPServerConfig : TransferConnectConfig
    {
        public UDPServerConfig(int localPort)
        {
            if (localPort <= 0)
            {
                throw new ArgumentOutOfRangeException("localPort");
            }

            LocalPort = localPort;
            BufferLength = 8192;
        }

        public int LocalPort { set; get; }

        public override TransferProtocolType Protocol
        {
            get { return TransferProtocolType.UDP_Normal; }
        }
    }

}
